#pragma once

#include <glibmm.h>
#include <stack>

#include "../Shared/GObjHelpers.hpp"
#include "../Shared/NamedObject.hpp"
#include "../Shared/Exception.hpp"

namespace BuildNG
{
  class Module;
  class ErrorListener;

  class Project : public NamedObject
  {
  public:
    ~Project();

    static Glib::RefPtr<Project> load_from_file(const Glib::ustring &src_file_name);

    bool build(const Glib::ustring &output_root_dir,
      Glib::RefPtr<ErrorListener> error_listener);

    int                       get_num_modules() const;
    
    // returns the module with the given name, or NULL if no 
    // such module exists.
    Glib::RefPtr<Module>      get_module(const Glib::ustring &name);
    std::vector<Glib::RefPtr<Module> > get_modules_list();

    const Glib::ustring&      get_root_dir() const { return root_dir; }

  private:
    Project(const Glib::ustring &src_file_name);

    Glib::ustring                   root_dir;
    NamedObjectCollection<Module>   modules;
  };

  enum OutputDirType
  {
    OutputDirType_ExecutableDir,
    OutputDirType_DataDir,
    OutputDirType_UserConfigDir,
  };
  
  class BuildContext
  {
    friend class Project;
    friend class Module;

  public:
    void copy_to_output_dir(const Glib::ustring &src_file_name,
      const Glib::ustring &relative_dest_file_name,OutputDirType out_dir_type);

    const Glib::ustring&    get_cur_dir() const;
    const Glib::ustring&    get_cur_tmp_dir() const;

  private:
    Glib::ustring           project_root_dir;
    Glib::ustring           tmp_root_dir;

    struct ModuleInfo
    {
      Glib::RefPtr<Module>    module;

      Glib::ustring           tmp_dir;
      Glib::ustring           cur_dir;
    };
    std::stack<ModuleInfo>  module_stack;    

    // for the Projec class:
    BuildContext(const Glib::ustring &project_root_dir,const Glib::ustring &tmp_root_dir);

    void begin();
    void end();
    
    void push_module(Glib::RefPtr<Module> module);
    void pop_module();

    Glib::ustring get_temp_dir_for_module(Glib::RefPtr<Module> module) const;
  };

  class BuildException : public Exception
  {
  public:
    BuildException(const Glib::ustring &msg)
      : Exception(msg)
    {
    }
  };
}
